/* Copyright (C) 2015-2016 HangZhou Zenzet Technology Co., Ltd.
 * All right reserved

 * File:crypto/locketlog.c
 * Author:guojianchuan/max
 * Date:2016-06-02
 
 */

/* system header */
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <stdarg.h>
#include <errno.h>
/* 3rd project header */

/* my project header */
#include "locketdef.h"
#include "locketcommon.h"
#include "locketerrno.h"
#include "locketlog.h"


#define LOG_MODULE_NAME_LEN                    32               /* 模块名称长度 */
#define LOG_FILE_NAME_LEN                      64               /* 日志文件名称长度 */
#define LOG_MODULE_NUM                         4                /* 模块最大数量 */
#define LOG_BUF_LEN                            1024             /* Buffer长度 */
#define LOG_FILE_LEN                           (32*1024)        /* 模块大小 */
#ifdef APPLE
#define LOG_DEFAULT_LOG_PATH                   "/tmp/"             /* 日志文件的路径 */
#else
#define LOG_DEFAULT_LOG_PATH                   "./"             /* 日志文件的路径 */
#endif
#define LOG_SUFFIX                             "00.log"         /* 日志文件名后缀(下同) */
#define LOG_SUFFIX_BAK                         "01.log"
#define LOG_SUFFIX_DEBUG                       "00.debug"
#define LOG_SUFFIX_DEBUG_BAK                   "01.debug"

//windows:
//#define filename(x) strrchr(x,'\\')?strrchr(x,'\\')+1:x
//linux:
#define FILENAME(x) strrchr(x,'/')?strrchr(x,'/')+1:x

/**
* @Struct:LOG_MODULE_S
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description:维护每一个模块的日志信息
* @caution: 
*/
typedef struct tagLogModuleInfo
{
    LOG_LEVEL_E     enLogLevel;                                  /* 日志级别 */
    int             iModuleID;                                   /* 日志模块ID */
    char            szModuleName[LOG_MODULE_NAME_LEN];           /* 模块名 */
    int             iDebug;                                      /* 是否开启调试 */
    FILE           *pLogFile;                                    /* 日志文件句柄 */
    FILE           *pDebugLogFile;                               /* 调试日志文件句柄 */
//  pthread_mutex_t stMutex;                                     /* 线程锁 (暂时不用)*/
    int             iLogFileSize;                                /* 日志文件大小 */
    int             iDebugLogFileSize;                           /* 调试日志文件大小 */
    char            szLogFileName[LOG_FILE_NAME_LEN];            /* 日志文件名 */
    char            szBakLogFileName[LOG_FILE_NAME_LEN];         /* 备份的日志文件名 */
    char            szDebugLogFileName[LOG_FILE_NAME_LEN];       /* 调试日志文件名 */
    char            szBakDebugLogFileName[LOG_FILE_NAME_LEN];    /* 备份的调试日志文件名 */
}LOG_MODULE_S;

/**
* @Struct:LOG_INFO_E
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description:用于维护所有模块的日志信息
* @caution: 
*/
typedef struct tagLogInfo
{
    int          iModuleNum;                                     /* 有多少个日志文件模块 */
    LOG_MODULE_S astLogModuleInfo[LOG_MODULE_NUM];               /* 日志模块信息 */
}LOG_S;

/**
* @Variable:gaszLogLevel
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: 日志级别的枚举转换成字符串 
* @caution: 
*/
char gaszLogLevel[][16] = {"FATAL", "ERROR", "WARN", "DEBUG", "INFO"};  

/**
* @Variable:gstLogInfo
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: 
* @caution: 
*/
static LOG_S gstLogInfo;

/**
* @Function:LOCKET_LOG_Init
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: Log init
* @caution: 
*/
static void LOCKET_LOG_Init (void)
{
    memset(&gstLogInfo, 0, sizeof(LOG_S));
    return;
}

/**
* @Function:LOCKET_LOG_GetFileSize
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: Get log file size
* @caution: 
*/
static int LOCKET_LOG_GetFileSize(IN char *pcFileName)
{
    if (NULL == pcFileName)
    {
        return 0;
    }

    struct stat stStat = {0};

    /* 获取文件大小 */
    if (0 == stat(pcFileName, &stStat))
    {
        return stStat.st_size;
    }
    else
    {
        return 0;
    }
}

/**
* @Function:LOCKET_LOG_RegisterModule
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: register log module
* @caution: 
*/
void LOCKET_LOG_RegisterModule(IN int iLogModuleID, IN char *pcLogModuleName)
{
    int iInitOK = 0;
    if (!iInitOK)
    {
        /* 日志初始化 */
        LOCKET_LOG_Init ();

        iInitOK = 1;
    }

    LOG_MODULE_S *pstModuleInfo = NULL;

    if ((LOG_MODULE_NUM == gstLogInfo.iModuleNum) || (NULL == pcLogModuleName))
    {
        return;
    }

    pstModuleInfo = &gstLogInfo.astLogModuleInfo[gstLogInfo.iModuleNum];

    pstModuleInfo->iModuleID                    = iLogModuleID;
    pstModuleInfo->iDebug                       = 0;
    pstModuleInfo->enLogLevel                   = LOG_LEVEL_INFO;
    pstModuleInfo->pLogFile                     = NULL;
    pstModuleInfo->pDebugLogFile                = NULL;

#if 0
    pthread_mutex_init(&pstModuleInfo->stMutex, NULL);
#endif

    strncpy(pstModuleInfo->szModuleName, pcLogModuleName, sizeof(pstModuleInfo->szModuleName) - 1);
    snprintf(pstModuleInfo->szLogFileName, sizeof(pstModuleInfo->szLogFileName), "%s%s%s",
             LOG_DEFAULT_LOG_PATH, pcLogModuleName, LOG_SUFFIX);
    snprintf(pstModuleInfo->szBakLogFileName, sizeof(pstModuleInfo->szBakLogFileName), "%s%s%s",
             LOG_DEFAULT_LOG_PATH, pcLogModuleName, LOG_SUFFIX_BAK);
    snprintf(pstModuleInfo->szDebugLogFileName, sizeof(pstModuleInfo->szDebugLogFileName), "%s%s%s",
             LOG_DEFAULT_LOG_PATH, pcLogModuleName, LOG_SUFFIX_DEBUG);
    snprintf(pstModuleInfo->szBakDebugLogFileName, sizeof(pstModuleInfo->szBakDebugLogFileName), "%s%s%s",
             LOG_DEFAULT_LOG_PATH, pcLogModuleName, LOG_SUFFIX_DEBUG_BAK);

    pstModuleInfo->iLogFileSize        = LOCKET_LOG_GetFileSize(pstModuleInfo->szLogFileName);
    pstModuleInfo->iDebugLogFileSize   = LOCKET_LOG_GetFileSize(pstModuleInfo->szDebugLogFileName);

    gstLogInfo.iModuleNum++;

    return;
}


/**
* @Function:LOCKET_LOG_FindModule
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: find module
* @caution: 
*/
static LOG_MODULE_S *LOCKET_LOG_FindModule(IN int iLogModuleID)
{
    int ulModuleIndex                 = 0;
    LOG_MODULE_S *pstLogModuleInfo = NULL;

    for (ulModuleIndex = 0; ulModuleIndex < gstLogInfo.iModuleNum; ulModuleIndex++)
    {
        if (iLogModuleID == gstLogInfo.astLogModuleInfo[ulModuleIndex].iModuleID)
        {
            pstLogModuleInfo = &gstLogInfo.astLogModuleInfo[ulModuleIndex];
            break;
        }
    }

    return pstLogModuleInfo;
}

/**
* @Function:LOCKET_LOG_EnableDebug
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: Enable debug 
* @caution: 
*/
int LOCKET_LOG_EnableDebug(IN int iLogModuleID)
{
    LOG_MODULE_S *pstLogModuleInfo = NULL;

    /* 判断该模块是否已注册 */
    pstLogModuleInfo = LOCKET_LOG_FindModule(iLogModuleID);
    if (NULL == pstLogModuleInfo)
    {
        return ERR_LOG_NOT_FOUND;
    }

    pstLogModuleInfo->iDebug = 1;

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_LOG_DisableDebug
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: Disable debug 
* @caution: 
*/
int LOCKET_LOG_DisableDebug(IN int iLogModuleID)
{
    LOG_MODULE_S *pstLogModuleInfo = NULL;

    /* 判断该模块是否已注册 */
    pstLogModuleInfo = LOCKET_LOG_FindModule(iLogModuleID);
    if (NULL == pstLogModuleInfo)
    {
        return ERR_LOG_NOT_FOUND;
    }

    pstLogModuleInfo->iDebug = 0;

    return ERR_COMMON_SUCCESS;
}

/**
* @Function:LOCKET_LOG_Write
* @Author: guojianchuan/max
* @Date: 2016-06-14
* @Description: Write log to file
* @caution: 
*/
void LOCKET_LOG_Write(IN LOG_LEVEL_E enErrLevel, 
                      IN int iLogModuleID, 
                      IN char *pcFile, 
                      IN int iLine, 
                      IN char *pcFmt, ...)
{
    char               szLogBuf[LOG_BUF_LEN];  
    LOG_MODULE_S *pstLogModuleInfo              = NULL;
    FILE             **pLogFile                 = NULL;
    int             *pulLogFileSize           = NULL;
    char              *pcLogFileName            = NULL;
    char              *pcBakLogFileName         = NULL;
    int              ulWriteBufLen            = 0;
    int              ulWrittenLen             = 0;
    char              *pcModuleName             = NULL;
    struct timeval     tv;
    struct timezone    tz;
    va_list            pArgs;
    struct stat        stStat                   = {0};
    int               lResult                  = 0;

    if (NULL == pcFile)
    {
        return;
    }

    /* 判断该模块是否已注册 */
    pstLogModuleInfo = LOCKET_LOG_FindModule(iLogModuleID);
    if (NULL == pstLogModuleInfo)
    {
        return;
    }

    /* 判断日志级别是否达到要求 */
    if (((LOG_LEVEL_DEBUG == enErrLevel) && (0 == pstLogModuleInfo->iDebug)) ||
        ((LOG_LEVEL_DEBUG != enErrLevel) && (enErrLevel > pstLogModuleInfo->enLogLevel)))
    {
        return;
    }

    pcModuleName = pstLogModuleInfo->szModuleName;

    /* 选择日志文件 */
    if (0 == pstLogModuleInfo->iDebug)
    {
        pcLogFileName       = pstLogModuleInfo->szLogFileName;
        pcBakLogFileName    = pstLogModuleInfo->szBakLogFileName;
        pulLogFileSize      = &pstLogModuleInfo->iLogFileSize;
        pLogFile            = &pstLogModuleInfo->pLogFile;
    }
    else
    {
        pcLogFileName       = pstLogModuleInfo->szDebugLogFileName;
        pcBakLogFileName    = pstLogModuleInfo->szBakDebugLogFileName;
        pulLogFileSize      = &pstLogModuleInfo->iDebugLogFileSize;
        pLogFile            = &pstLogModuleInfo->pDebugLogFile;
    }

    /* 获取秒数 */
    gettimeofday(&tv, &tz);

    /* 获取日期 */
    time_t tiSecs = tv.tv_sec;
    struct tm stLocalTime = {0};
	struct tm *pstLocalTime = NULL;
#ifdef WIN32	
    pstLocalTime = localtime(&tiSecs);
#else
    pstLocalTime = localtime_r(&tiSecs, &stLocalTime);	
#endif

    /* 转换日期 */
    int lYear   = pstLocalTime->tm_year + 1900;
    int lMonth  = pstLocalTime->tm_mon + 1;
    int lDay    = pstLocalTime->tm_mday;
    int lHour   = pstLocalTime->tm_hour;
    int lMin    = pstLocalTime->tm_min;
    int lSec    = pstLocalTime->tm_sec;

    /* 将数据写入缓冲中 */
    ulWriteBufLen  = snprintf(szLogBuf, sizeof(szLogBuf), "%.4d-%.2d-%.2d %.2d:%.2d:%.2d %.3d [%s][%s-%d]",
                             lYear, lMonth, lDay, lHour, lMin, lSec, tv.tv_usec / 1000,
                             gaszLogLevel[enErrLevel], FILENAME(pcFile), iLine);

    va_start(pArgs, pcFmt);
    ulWriteBufLen += vsnprintf(szLogBuf + ulWriteBufLen, sizeof(szLogBuf) - ulWriteBufLen, pcFmt, pArgs);
    va_end(pArgs);

    /* 一条日志过多数据则丢弃后面的数据，2表示'\n'和'\0' */
    if (ulWriteBufLen > sizeof(szLogBuf) - 2)
    {
        ulWriteBufLen = sizeof(szLogBuf) - 2;
    }

    ulWriteBufLen += snprintf(szLogBuf + ulWriteBufLen, sizeof(szLogBuf) - ulWriteBufLen, "\n");

#if 0 
    if (0 != pthread_mutex_lock(&pstLogModuleInfo->stMutex))
    {
        return;
    }
#endif

    /* 如果日志文件写不下了，则重命名成备份日志文件 */
    if ((*pulLogFileSize + ulWriteBufLen) > LOG_FILE_LEN)
    {
        if (NULL != *pLogFile)
        {
            fclose(*pLogFile);
            *pLogFile = NULL;
        }

        if (0 != rename(pcLogFileName, pcBakLogFileName))
        {
            /* 重命名失败有可能是文件不存在了，重新获取文件大小以防止记录错误的文件大小 */
            *pulLogFileSize = LOCKET_LOG_GetFileSize(pcLogFileName);

           // pthread_mutex_unlock(&pstLogModuleInfo->stMutex);
            return;
        }
    }

    if (NULL == *pLogFile)
    {
        *pLogFile = fopen(pcLogFileName, "a");
        if (NULL == *pLogFile)
        {
            fprintf(stderr, "Open file %s failed:%s\n", pcLogFileName, strerror(errno));
            fflush(stdout);
            //   pthread_mutex_unlock(&pstLogModuleInfo->stMutex);
            return;
        }

        *pulLogFileSize = LOCKET_LOG_GetFileSize(pcLogFileName);
    }

    /* 将读取到的所有数据写入文件 */
    for (ulWrittenLen = 0; ulWrittenLen < ulWriteBufLen; )
    {
        //printf("log:%s", szLogBuf + ulWrittenLen);
        lResult = fprintf(*pLogFile, "%s", szLogBuf + ulWrittenLen);
        if (lResult < 0)
        {
            if (EINTR != errno)
            {
                fprintf(stderr, "Write log failed:%s\n", strerror(errno));
                fflush(stdout);
                break;
            }
        }
        else /* 成功的情况下代表写入的数据 */
        {
            ulWrittenLen += lResult;
        }
    }

    *pulLogFileSize += ulWrittenLen;

    /* 将缓存中的数据真正写入文件 */
    if (0 != fflush(*pLogFile))
    {
        fprintf(stderr, "Can't save log file:%s\n", strerror(errno));
        fflush(stdout);
    }

  //  pthread_mutex_unlock(&pstLogModuleInfo->stMutex);

    return;
}

